package edu.gatech.mobile.scene.ui.camera;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.opengl.Matrix;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.View;

public final class ViewfinderView extends View{

	private CameraManager cameraManager;
	private final Paint paint;
	private Bitmap resultBitmap;

	private SensorManager sensorManager;
	private Sensor accelerometer;
	private Sensor magnetometer;
	private LocationManager mLocationMan;
	private Location lastLocation;

	
	
	private float[] accelerometerValues;
	private float[] magnetometerValues;
	private List<Point> validPointList;
	private float[] rotation4;

	final SensorEventListener myAccelerometerListener = new SensorEventListener() {

		@Override
		public void onAccuracyChanged(Sensor arg0, int arg1) {

		}

		@Override
		public void onSensorChanged(SensorEvent event) {
			accelerometerValues = event.values;
		}
	};

	final SensorEventListener myMagneticFieldListener = new SensorEventListener() {
		public void onSensorChanged(SensorEvent sensorEvent) {
			if (sensorEvent.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) {
				magnetometerValues = sensorEvent.values;
			}
		}
		public void onAccuracyChanged(Sensor sensor, int accuracy) {
		}
	};
	
	final LocationListener locListener  = new LocationListener() {
		
		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void onProviderEnabled(String provider) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void onProviderDisabled(String provider) {
			// TODO Auto-generated method stub
			
		}
		
		@Override
		public void onLocationChanged(Location loc) {
			lastLocation = loc;
		}
	};

	// This constructor is used when the class is built from an XML resource.
	public ViewfinderView(Context context, AttributeSet attrs) {
		super(context, attrs);

		// Initialize these once for performance rather than calling them every
		// time in onDraw().
		paint = new Paint(Paint.ANTI_ALIAS_FLAG);
		paint.setColor(Color.RED);
		getContext();
		sensorManager = (SensorManager) getContext().getSystemService(
				Context.SENSOR_SERVICE);
		accelerometer = sensorManager
				.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
		magnetometer = sensorManager
				.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
		
		magnetometerValues = new float[3];
		accelerometerValues = new float[3];
		validPointList = new ArrayList<Point>();
		resumeViewfinder();

	}

	public void setCameraManager(CameraManager cameraManager) {
		this.cameraManager = cameraManager;
	}

	@Override
	public void onDraw(Canvas canvas){
		if(cameraManager.isPreviewing()){
			updateHorizon(canvas);
		}
	}

	private void updateHorizon(Canvas mCanvas) {

		validPointList = new ArrayList<Point>();
		rotation4= new float[16];
		float[] Rtrans = new float[16];
		
		SensorManager.getRotationMatrix(rotation4, null, accelerometerValues,
				magnetometerValues);

		Matrix.transposeM(Rtrans, 0, rotation4, 0);
		for (int i = 0; i < 100; i++) {
			float circleTheta = (float) (2.0 * Math.PI * ((float) i) / 100.0f);
			float[] point4 = { 100.0f * (float) FloatMath.cos(circleTheta),
					100.0f * (float) FloatMath.sin(circleTheta), 0.0f, 1.0f };
			projectAndDrawPoint(mCanvas, point4);
		}
	}

	private void projectAndDrawPoint(Canvas mCanvas, float[] point) {
		float[] rotatedPoint = new float[4];
		Matrix.multiplyMV(rotatedPoint, 0, rotation4, 0, point, 0);

		
		//TODO JW calibrate to correct horizon
		// Focal length 900
		float pointX = mCanvas.getWidth() / 2.0f + 900.0f
				* (rotatedPoint[1] / rotatedPoint[2]);
		float pointY = mCanvas.getHeight() / 2.0f + 900.0f
				* (rotatedPoint[0] / rotatedPoint[2]);

		if (rotatedPoint[2] > 0.0) {
			drawPoint(mCanvas, pointX, pointY);
			//For testing only
//			float newX = (pointX / mCanvas.getWidth()) * 640;
//			float newY = (pointY / mCanvas.getHeight()) * 480;

			if (pointX > 0 && pointX < mCanvas.getWidth()) {
				Point newPoint = new Point((int) pointX, (int) pointY);
				validPointList.add(newPoint);
			}
		}
	}
	
	
    private void drawPoint(Canvas mCanvas, float x, float y){
    	mCanvas.drawCircle(x, y, 10, paint);
    	mCanvas.save();
    }

    public void drawViewfinder() {
		Bitmap resultBitmap = this.resultBitmap;
		this.resultBitmap = null;
		if (resultBitmap != null) {
			resultBitmap.recycle();
		}
		invalidate();
	}

	public void pauseViewfinder() {
		sensorManager.unregisterListener(myAccelerometerListener);
		sensorManager.unregisterListener(myMagneticFieldListener);
		mLocationMan.removeUpdates(locListener);
	}
	public void resumeViewfinder() {
		sensorManager.registerListener(myAccelerometerListener, accelerometer,
				SensorManager.SENSOR_DELAY_UI);
		sensorManager.registerListener(myMagneticFieldListener, magnetometer,
				SensorManager.SENSOR_DELAY_UI);
		
		Criteria criteria = new Criteria();	
		mLocationMan = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
		String bestProvider = mLocationMan.getBestProvider(criteria, true);
		mLocationMan.requestLocationUpdates(bestProvider, 30000, 10, locListener);
	}
	
	public List<Point> getHorizonPoints(){
		List<Point> points =new ArrayList<Point>(); 
		points.addAll(validPointList);
		return points;
	}
	
	public float[] getAccelerometerValues() {
		return accelerometerValues.clone();
	}

	public float[] getMagnetometerValues() {
		return magnetometerValues.clone();
	}

	public Location getLastLocation() {
		Criteria criteria = new Criteria();
		Location myLoc = lastLocation;
		if (myLoc == null) {
			String provider = mLocationMan.getBestProvider(criteria,true);
			myLoc = mLocationMan.getLastKnownLocation(provider);
		}
		return myLoc;
	}
	
	
}
